package com.atmilan.quartz.utils.product;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.atmilan.quartz.config.RestTemplateConfig;
import com.atmilan.quartz.model.dto.guanjp.GuanjpProductResDTO;
import com.atmilan.quartz.model.dto.guanjp.GuanjpProductSkuResDTO;
import com.atmilan.quartz.utils.DateUtil;
import com.atmilan.quartz.utils.unibuy.util.MD5Util;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 拉取商品源数据
 */

@Slf4j
@Service
public class GuanjpProductUtil {
    @Autowired
    private RestTemplateConfig restTemplateConfig;

	private static final String cipherMode="AES/CBC/PKCS7Padding";
    private static final String encryptAlg ="AES";
    private static final String encode="UTF-8";
    
    @Value("${guanjp.appsecret}")
    private String appsecret;
    @Value("${guanjp.iv}")
    private String iv;
    @Value("${guanjp.appkey}")
    public String appkey;
    @Value("${guanjp.signKey}")
    public String signKey;
    @Value("${guanjp.refreshToken}")
    public String refreshToken;
    @Value("${guanjp.url.domain}")
    private String domianUrl;
    @Value("${guanjp.url.token}")
    private String tokenUrl;
    @Value("${guanjp.method.products}")
    private String productsMethod;
    @Value("${guanjp.method.stocks}")
    private String stocksMethod;

    // 国内仓: 430047177030221825(主仓库(大仓库）), 1094417626916518687(瑕疵仓), 588323880157821034(代卖仓), 588322641211843528(小仓库), 430047177030221826(明通仓库)
	// 香港仓: 1189838447889696442(香港陈好仓)
    public static final List<String> ktypeidCNs = Arrays.asList("430047177030221825", "1094417626916518687", "588323880157821034", "588322641211843528", "430047177030221826");
    public static final List<String> ktypeidHKs = Arrays.asList("1189838447889696442");
    
    /**
     * 刷新token
     * @author daigb
     * @date:  2022-12-14 17:25
     */
    public String refreshToken() {
		// 加密
        String p = encrypt(JSON.toJSONString(Param.getThis(refreshToken)));
        // 获取sign
        String sign = getSha256Str(JSON.toJSONString(SignParam.getThis(appkey, signKey, p)));
        Map<String, Object> map = new HashMap<>();
        map.put("appkey", appkey);
        map.put("p", p);
        map.put("sign", sign);
        // 发送请求
		String result = sendRequest(map, tokenUrl);
        log.info("--------------- 管家婆刷新token返回: {} ---------------", result);
        JSONObject resultObject = JSON.parseObject(result);
        // 解密token
        String code = decrypt(resultObject.getJSONObject("response").getString("response"));
        String authToken = JSON.parseObject(code).getString("auth_token");
        
        return authToken;
    }

    /**
     * @throws JsonProcessingException 
     * @throws Exception 
     * 查询分页商品数据
     * @Description:
     * @author daigb
     * @param @param pageNow
     * @param @param pageSize
     * @param @return
     * @param @throws Exception
     * @return InProductResDTO
     * @throws
     */
    public GuanjpProductResDTO selectPage(Integer pageNo, Integer pageSize, String token) throws Exception {
    	// 加密请求业务参数
		Map<String, Object> signMap = getParame(token, productsMethod);
		signMap.put("pageno", pageNo);
		signMap.put("pagesize", pageSize);
		signMap.put("sign", getSign(signMap));
    	
		// 发送请求
		String result = sendRequest(signMap, domianUrl);
        // 对象转换
        ObjectMapper mapper = new ObjectMapper();
        GuanjpProductResDTO guanjpProductResDTO = mapper.readValue(result, GuanjpProductResDTO.class);
        
        return guanjpProductResDTO;
    }

    /**
     * 获取商品sku列表
     * @param productId
     * @return
     */
    public GuanjpProductSkuResDTO getSkus(List<String> productIds, String token) throws Exception {
    	// 加密请求业务参数
    	List<String> ktypeids = new ArrayList<String>();
    	ktypeids.addAll(ktypeidCNs);
    	ktypeids.addAll(ktypeidHKs);
		Map<String, Object> signMap = getParame(token, stocksMethod);
		signMap.put("pageno", 1);
		signMap.put("pagesize", 100);
		signMap.put("ptypeids", productIds);
		signMap.put("ktypeids", ktypeids);
		signMap.put("sign", getSign(signMap));

		// 发送请求
		String result = sendRequest(signMap, domianUrl);
        // 对象转换
        ObjectMapper mapper = new ObjectMapper();
        GuanjpProductSkuResDTO guanjpProductSkuResDTO = mapper.readValue(result, GuanjpProductSkuResDTO.class);
        
        return guanjpProductSkuResDTO;
    }
    
    
    /****************** 工具方法 ******************/
    /**
     * 初始化参数
     * @author daigb
     * @date:  2022-12-15 11:30
     */
    private Map<String, Object> getParame(String token, String method) {
		String timeStr = DateUtil.nowDateTimeStr();
		Map<String, Object> signMap = new HashMap<>();
	    signMap.put("appkey", appkey);
	    signMap.put("token", token);
	    signMap.put("method", method);
	    signMap.put("timestamp", timeStr);
	    
	    return signMap;
    }
    
    /**
     * 发送公共请求
     * @author daigb
     * @date:  2022-12-15 11:35
     */
    private String sendRequest(Map<String, Object> signMap, String url) {
    	// 发送请求
		RestTemplate restTemplate = restTemplateConfig.restTemplate();
		HttpHeaders headers = new HttpHeaders();
		headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED_VALUE);
		// 设置请求参数
		MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
		signMap.forEach((key, value) -> {
	    	postParameters.add(key, value);
		});
		HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(postParameters, headers);
		ResponseEntity<String> response = restTemplate.postForEntity(url, httpEntity, String.class);
		String result = response.getBody();
//        log.info("--------------- 管家婆查询接口返回: {} ---------------", result);
        
        return result;
    }
    
    /**
     * 加密：有向量16位，结果转base64
     * @param context
     * @return
     */
    private String encrypt(String context) {
        try {
            //下面这行在进行PKCS7Padding加密时必须加上，否则报错
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            byte[] content=context.getBytes(encode);
            SecretKeySpec key = new SecretKeySpec(appsecret.getBytes(encode), encryptAlg);
            Cipher cipher = Cipher.getInstance(cipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv.getBytes(encode)));
            byte[] data = cipher.doFinal(content);
            String result= Base64.encodeBase64String(data);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密
     * @param context
     * @return
     */
    private String decrypt(String context) {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            byte[] data=Base64.decodeBase64(context);
            SecretKeySpec key = new SecretKeySpec(appsecret.getBytes(encode), encryptAlg);
            Cipher cipher = Cipher.getInstance(cipherMode);
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv.getBytes(encode)));
            byte[] content = cipher.doFinal(data);
            String result=new String(content,encode);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * sha256加密
     *
     * @param str 要加密的字符串
     * @return 加密后的字符串
     */
    private String getSha256Str(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return encodeStr;
    }

    /**
     * sha256加密 将byte转为16进制
     *
     * @param bytes 字节码
     * @return 加密后的字符串
     */
    private String byte2Hex(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        String temp;
        for (byte aByte : bytes) {
            temp = Integer.toHexString(aByte & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                stringBuilder.append("0");
            }
            stringBuilder.append(temp);
        }
        return stringBuilder.toString();
    }
	
    /**
     * 参数签名
     * @author daigb
     * @date:  2022-12-15 10:37
     */
	public String getSign(Map<String, Object> signMap) {
	    TreeMap<String, Object> treemap = new TreeMap<>(signMap);
	    StringBuilder signString = new StringBuilder();
	    for (Map.Entry<String, Object> entry : treemap.entrySet()) {
	        signString.append(entry.getKey()).append(entry.getValue());
	    }
	    signString.append(signKey);

	    return MD5Util.encrypt(signString.toString());
	}

}

@Data
class Param {
    @JSONField(name = "TimeStamp")
    private String timeStamp;

    @JSONField(name = "GrantType")
    private String grantType;

    @JSONField(name = "AuthParam")
    private String authParam;
    
    public static Param getThis(String refreshToken) {
    	Param param = new Param();
    	param.setTimeStamp(DateUtil.nowDateTimeStr());
    	param.setGrantType("refresh_token");
    	param.setAuthParam(refreshToken);
    	
    	return param;
    }
    
}

@Data
class SignParam {
	
    @JSONField(name = "appkey")
    private String appKey;

    @JSONField(name = "signkey")
    private String signKey;

    @JSONField(name = "p")
    private String p;
    
    public static SignParam getThis(String appKey, String signKey, String p) {
    	SignParam signParam = new SignParam();
    	signParam.setAppKey(appKey);
    	signParam.setSignKey(signKey);
    	signParam.setP(p);
    	
    	return signParam;
    }
}
